package control.reader;

import java.awt.Color;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Properties;

import control.profiler.Profiler;
import control.render.RenderSetup;


import data.base.PointLight;
import data.base.Vector3;
import data.complex.Camera;
import data.hierarchy.Mesh;
import data.hierarchy.World;

public class RenderSetupReader {
  private Properties properties;
  private RenderSetup renderSetup;
  private World world;
  
  private static final String PROP_RENDERER = "renderer";
  private static final String PROP_DIMENSION = "dimension";
  private static final String PROP_THREADCOUNT = "threadcount";
  private static final String PROP_BUCKETSIZE = "bucketsize";
  private static final String PROP_UNBIASED = "unbiased";  
  private static final String PROP_MESH = "mesh";
  private static final String PROP_FILE = "file";
  private static final String PROP_LIGHT = "light";
  private static final String PROP_POSITION = "position";
  private static final String PROP_COLOR = "color";
  private static final String PROP_INTENSITY = "intensity";
  private static final String PROP_CAMERA = "camera";
  private static final String PROP_TARGETMESH = "targetmesh";
  private static final String PROP_FOV = "fov";
  private static final String PROP_FOCUS = "focus";  
  private static final String PROP_APERATURE = "aperature";
  private static final String PROP_ENVIRONMENT = "environment";
  private static final String PROP_TRACEDEPTH = "tracedepth";
  private static final String PROP_RAYSPERINTERSECTION = "raysperintersection";
  
  private static final String PROP_VAL_AUTO = "auto";
  private static final String PROP_VAL_OFF = "off";
  private static final String PROP_VAL_YES = "yes";
  private static final String PROP_VAL_NO = "no";
  
  
  public RenderSetupReader(String file){
    try {
      properties = new Properties();
      BufferedInputStream stream = new BufferedInputStream(new FileInputStream(file));
      properties.load(stream);      
    }
    catch (IOException e) {
      e.printStackTrace();
    }
  }
  
  public World getWorld(){
    if(world == null){
      world = new World();      
      readMeshes(world);
      readLights(world);
      readCamera(world);
    }    
    return world;
  }
  
  public RenderSetup getRenderSetup(){
    if(renderSetup == null){
      renderSetup = new RenderSetup();
      readRenderSetup();
    }
    return renderSetup;
  }
  
  private void readRenderSetup(){    
    String dimString = properties.getProperty(PROP_RENDERER + "." + PROP_DIMENSION);
    if(dimString != null){
      String[] dim = dimString.split(",");
      renderSetup.setDimension(
          Integer.parseInt(dim[0].trim()), 
          Integer.parseInt(dim[1].trim()));
    }
    String threadCount = properties.getProperty(PROP_RENDERER + "." + PROP_THREADCOUNT);
    if(threadCount != null){
      renderSetup.setThreadCount(Integer.parseInt(threadCount));
    }
    String bucketSize = properties.getProperty(PROP_RENDERER + "." + PROP_BUCKETSIZE);
    if(bucketSize != null){
      renderSetup.setBucketSize(Integer.parseInt(bucketSize));
    }
    String focus = properties.getProperty(PROP_CAMERA + "." + PROP_FOCUS);
    if(focus != null){
      if(focus.toLowerCase().equals(PROP_VAL_AUTO))
        renderSetup.setFocalLength(RenderSetup.AUTO_FOCUS);
      else if(focus.toLowerCase().equals(PROP_VAL_OFF))
        renderSetup.setFocalLength(RenderSetup.FOCUS_OFF);
      else
        renderSetup.setFocalLength(Double.parseDouble(focus));
    }
    String aperature = properties.getProperty(PROP_CAMERA + "." + PROP_APERATURE);
    if(aperature != null){
      renderSetup.setAperature(Double.parseDouble(aperature));
    }
    String unbiased = properties.getProperty(PROP_RENDERER + "." + PROP_UNBIASED);
    if(unbiased != null){
      if(unbiased.toLowerCase().equals(PROP_VAL_YES))
        renderSetup.setUnbiased(true);
      else if(unbiased.toLowerCase().equals(PROP_VAL_NO))
        renderSetup.setUnbiased(false);
    }
    String environment = properties.getProperty(PROP_RENDERER + "." + PROP_ENVIRONMENT);
    if(environment != null){
      renderSetup.setEnvironment(Double.parseDouble(environment));
    }
    String traceDepth = properties.getProperty(PROP_RENDERER + "." + PROP_TRACEDEPTH);
    if(traceDepth != null){
      renderSetup.setTraceDepth(Integer.parseInt(traceDepth));
    }
    String raysPerIntersection = properties.getProperty(PROP_RENDERER + "." + PROP_RAYSPERINTERSECTION);
    if(raysPerIntersection != null){
      renderSetup.setRaysPerIntersection(Integer.parseInt(raysPerIntersection));
    }
  }
  
  private void readMeshes(World world){
    for(int i = 0;; i++){
      String meshFile = properties.getProperty(PROP_MESH + i + "." + PROP_FILE);
      if(meshFile == null) break;
      
      Profiler.getInstance().record("Reading mesh " + (i + 1) + "...");
      ObjReader objReader = new ObjReader();
      Mesh m = objReader.readFile(meshFile.trim());
      Profiler.getInstance().display();
      
      world.addMesh(m);
    }
  }
  
  private void readLights(World world){
    for(int i = 0;; i++){      
      String lightPosition = properties.getProperty(PROP_LIGHT + i + "." + PROP_POSITION);
      if(lightPosition == null) break;           
      String[] lightPos = lightPosition.split(",");
      PointLight light = new PointLight(new Vector3(
          Double.parseDouble(lightPos[0].trim()),
          Double.parseDouble(lightPos[1].trim()),
          Double.parseDouble(lightPos[2].trim())));
      
      String lightIntensity = properties.getProperty(PROP_LIGHT + i + "." + PROP_INTENSITY);
      if(lightIntensity != null)
        light.setIntensity(Double.parseDouble(lightIntensity.trim()));
      
      String lightColor = properties.getProperty(PROP_LIGHT + i + "." + PROP_COLOR);
      if(lightColor != null){
        String[] lightCol = lightColor.split(",");
        light.setColor(new Color(
            Integer.parseInt(lightCol[0].trim()),
            Integer.parseInt(lightCol[1].trim()),
            Integer.parseInt(lightCol[2].trim())));
      }
      world.addPointLight(light);
    }
  }
  
  private void readCamera(World world){
    String[] cameraPos = properties.getProperty(PROP_CAMERA + "." + PROP_POSITION).split(",");
    String cameraFov = properties.getProperty(PROP_CAMERA + "." + PROP_FOV);
    double fov = (cameraFov == null) ? 45 : Double.parseDouble(cameraFov.trim());
    Camera camera = new Camera(new Vector3(
        Double.parseDouble(cameraPos[0].trim()),
        Double.parseDouble(cameraPos[1].trim()),
        Double.parseDouble(cameraPos[2].trim())),
        fov);
    
    String target = properties.getProperty(PROP_CAMERA + "." + PROP_TARGETMESH);
    camera.setTarget(world.getMeshes().get(Integer.parseInt(target.trim())).getBoundary());
    
    world.setCamera(camera);
  }
}
